home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.2a2 / WELineLayout.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-12  |  15.7 KB  |  559 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELineLayout.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Line Layout, Getting and Setting Variables, etc.
  6.  *
  7.  *  Copyright (c) 1993-1995 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #ifndef __PALETTES__
  18. #include <Palettes.h>
  19. #endif
  20.  
  21. #ifndef __QDOFFSCREEN__
  22. #include <QDOffscreen.h>
  23. #endif
  24.  
  25. #ifndef __GESTALT__
  26. #include <Gestalt.h>
  27. #endif
  28.  
  29. pascal void WEStopInlineSession(WEHandle hWE)
  30. {
  31.     WEPtr pWE = *hWE;
  32.  
  33. // call FixTSMDocument() only if the inlint input area is actually "open"
  34.     if ((pWE->tsmAreaStart != kInvalidOffset) && (pWE->tsmReference != NULL))
  35.         FixTSMDocument(pWE->tsmReference);
  36. }
  37.  
  38. /*inline*/ pascal void _WERemoveLine(long lineIndex, WEPtr pWE)
  39. {
  40.     // remove the line
  41.     _WERemoveBlock((Handle) pWE->hLines, sizeof(LineRec), lineIndex * sizeof(LineRec));
  42.  
  43.     // decrement line count
  44.     pWE->nLines--;
  45. }
  46.  
  47. pascal OSErr _WEInsertLine(long lineIndex, const LineRec *theLine, WEPtr pWE)
  48. {
  49.     // insert the specified element in the line array
  50.     OSErr err;
  51.  
  52.     // do the insertion
  53.     if ((err = _WEInsertBlock((Handle) pWE->hLines, theLine, sizeof(LineRec), lineIndex * sizeof(LineRec))) != noErr)
  54.         return err;
  55.  
  56.     // increment line count
  57.     pWE->nLines++;
  58.     return noErr;
  59. }
  60.  
  61. pascal void _WEBumpOrigin(long lineIndex, long deltaOrigin, WEPtr pWE)
  62. {
  63.     LineRec *pLine = *pWE->hLines + lineIndex;
  64.     long nLines = pWE->nLines;
  65.  
  66.     // loop through the line run array adjusting the lineOrigin fields
  67.     for ( ; lineIndex <= nLines; lineIndex++ )
  68.     {
  69.         pLine->lineOrigin += deltaOrigin;
  70.         pLine++;
  71.     }
  72. }
  73.  
  74. pascal long _WEFindLineBreak(long lineStart, WEHandle hWE)
  75. {
  76.     // Find where to break the line beginning at lineStart
  77.     // the current graphics port must be already set up correctly
  78.  
  79.     WEPtr pWE = *hWE;    // assume WE record is already locked
  80.     Ptr pText;
  81.     long offset, breakOffset;
  82.     long textLength;
  83.     long remainingLength;
  84.     long segmentStart, segmentEnd;
  85.     long runIndex;
  86.     WERunInfo runInfo;
  87.     Fixed pixelWidth;
  88.     ScriptCode script, previousScript;
  89.     Boolean isBreak = false;
  90.  
  91.     offset = lineStart;
  92.     pText = *pWE->hText + offset;
  93.     remainingLength = pWE->textLength - offset;
  94.  
  95.     // find the style run index corresponding to the first segment on this line
  96.     runIndex = _WEOffsetToRun(offset, hWE);
  97.  
  98.     // initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity
  99.     pixelWidth = BSL((pWE->destRect.right - pWE->destRect.left), 16);
  100.  
  101.     // STYLE SEGMENT LOOP
  102.     do
  103.     {
  104.  
  105.         // get style run information for the current style run
  106.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  107.         runIndex++;
  108.  
  109.         // set text attributes in the graphics port
  110.         TextFont(runInfo.runAttrs.runStyle.tsFont);
  111.         TextFace(runInfo.runAttrs.runStyle.tsFace);
  112.         TextSize(runInfo.runAttrs.runStyle.tsSize);
  113.  
  114.         // if we're handling multiscript text, keep track of script boundaries
  115.         if (BTST(pWE->flags, weFNonRoman))
  116.         { 
  117.             // what is the script for this segment?
  118.             script = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  119.  
  120.             // have we crossed a script run boundary in the middle of a line?
  121.             if ((runInfo.runStart > offset) && (script != previousScript))
  122.             {
  123.                 // leave behind the all previous segments on this line
  124.                 offset = runInfo.runStart;
  125.                 pText = *pWE->hText + offset;
  126.                 remainingLength = pWE->textLength - offset;
  127.             }
  128.             previousScript = script;
  129.         } // if non-Roman
  130.  
  131.         // we'll pass textLength as the second parameter to the line break hook
  132.         // although this parameter is declared as a long, StyledLineBreak uses only
  133.         // the low word, so make sure it doesn't trespass the 32,767 byte threshold!
  134.         textLength = _WEPinInRange(remainingLength, 0, SHRT_MAX);
  135.  
  136.         // calculate segmentStart and segmentEnd relative to offset
  137.         segmentStart = _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  138.         segmentEnd = _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  139.  
  140.         // set breakOffset to a non-zero value for the first script run on the line,
  141.         // set it to zero for all subsequent script runs
  142.         breakOffset = (offset == lineStart);
  143.  
  144. #if WASTE_OBJECTS
  145.         if (runInfo.runAttrs.runStyle.tsObject != NULL)
  146.         {
  147.             // EMBEDDED OBJECT
  148.             // subtract object width from pixelWidth
  149.             pixelWidth -= BSL((*runInfo.runAttrs.runStyle.tsObject)->objectSize.h, 16);
  150.             
  151.             // stop looping if pixelWidth has gone negative
  152.             isBreak = (pixelWidth < 0);
  153.             breakOffset = isBreak ? segmentStart : segmentEnd;
  154.         }
  155.         else
  156. #endif
  157.         {
  158.             // REGULAR TEXT
  159.             isBreak = (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd,
  160.                 &pixelWidth, &breakOffset, hWE, pWE->lineBreakHook) != smBreakOverflow);
  161.         }
  162.  
  163.         // break the line anyway when we reach the end of the text
  164.         if (segmentEnd >= remainingLength)
  165.             isBreak = true;
  166.  
  167.     } while (!isBreak);
  168.  
  169.     // return the offset from lineStart to the break point
  170.     return (offset - lineStart) + breakOffset;
  171. }
  172.  
  173. pascal void _WECalcHeights(long rangeStart, long rangeEnd, short *lineAscent, short *lineDescent,
  174.         WEHandle hWE)
  175. {
  176.     // Find the maximum ascent and descent values between rangeStart and rangeEnd
  177.     // the WE record must be already locked
  178.     // the current graphics port must be already set up correctly
  179.  
  180.     long runIndex;
  181.     WERunInfo runInfo;
  182.     short runAscent, runDescent;
  183.  
  184.     *lineAscent = 1;
  185.     *lineDescent = 1;
  186.  
  187.     // find the style run index corresponding to the first segment on this line
  188.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  189.  
  190.     // STYLE SEGMENT LOOP
  191.     do
  192.     {
  193.         // get style run information for the current style run
  194.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  195.         runIndex++;
  196.  
  197.         // calculate ascent and descent (actually, descent + leading) values for this style run
  198.  
  199. #if WASTE_OBJECTS
  200.         if (runInfo.runAttrs.runStyle.tsObject != NULL)
  201.         {
  202.             // EMBEDDED OBJECT
  203.             runAscent = (*runInfo.runAttrs.runStyle.tsObject)->objectSize.v;
  204.             runDescent = 0;
  205.         }
  206.         else
  207. #endif
  208.         {
  209.             // REGULAR TEXT
  210.             runAscent = runInfo.runAttrs.runAscent;
  211.             runDescent = runInfo.runAttrs.runHeight - runAscent;
  212.         }
  213.         
  214.         // save the maximum values in lineAscent and lineDescent
  215.         if (runAscent > *lineAscent) 
  216.             *lineAscent = runAscent;
  217.  
  218.         if (runDescent > *lineDescent) 
  219.             *lineDescent = runDescent;
  220.         
  221.         // keep looping until we reach rangeEnd
  222.     } while (runInfo.runEnd < rangeEnd);
  223. }
  224.  
  225. pascal OSErr _WERecalBreaks(long *startLine, long *endLine, WEHandle hWE)
  226. {
  227.     // Recalculates line breaks, line heights and ascents for all the text or for a portion of it.
  228.     // On entry, startLine and endLine define a range of lines to recalculate.
  229.     // On exit, startLine to endLine defines the range of lines actually recalculated
  230.     // the WE record must already be locked
  231.  
  232.     WEPtr pWE = *hWE;
  233.     LinePtr pLine;
  234.     LineRec lineInfo, oldLineInfo;
  235.     long lineIndex;
  236.     long recalThreshold;
  237.     long lineOffset;
  238.     short lineAscent, lineDescent;
  239.     long textHeight;
  240.     Boolean saveTextLock;
  241.     QDEnvironment saveEnvironment;
  242.     OSErr err = noErr;
  243.  
  244.     // lock the text
  245.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  246.  
  247.     // find the character offset that must be necessarily reached before we can
  248.     // even consider the possibility of stopping the recalculation process
  249.     // this offset, recalThreshold, is the last character on endLine _before_ recalculation
  250.     lineIndex = _WEPinInRange(*endLine, 0, pWE->nLines - 1);
  251.     recalThreshold = (*pWE->hLines)[lineIndex + 1].lineStart;
  252.  
  253.     // we start recalculating line breaks from the line actually _preceding_ startLine,
  254.     // since editing startLine may cause part of its text to fit on the preceding line
  255.     lineIndex = _WEPinInRange(*startLine - 1, 0, pWE->nLines - 1);
  256.  
  257.     // find where in the text recalculation should begin
  258.     lineInfo = (*pWE->hLines)[lineIndex];
  259.  
  260.     // save the QuickDraw environment
  261.     _WESaveQDEnvironment(pWE->port, false, &saveEnvironment);
  262.  
  263.     // MAIN LINE BREAKING LOOP
  264.     do
  265.     {
  266.         // find where to break the current line
  267.         lineOffset = _WEFindLineBreak(lineInfo.lineStart, hWE);
  268.  
  269.         // make sure we advance at least by one character (unless we reached the end of text)
  270.         if ((lineOffset <= 0) && (lineInfo.lineStart < pWE->textLength))
  271.             lineOffset = 1;
  272.         
  273.         // calculate ascent and descent values for this line
  274.         _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, &lineAscent, &lineDescent, hWE);
  275.  
  276.         // save the maximum line ascent for this line in the line array
  277.         pLine = *pWE->hLines + lineIndex;
  278.         pLine->lineAscent = lineAscent;
  279.  
  280.         // increment counters (go to the next line array entry)
  281.         lineIndex++;
  282.         lineInfo.lineStart += lineOffset;
  283.         lineInfo.lineOrigin += (lineAscent + lineDescent);
  284.         pLine++;
  285.         
  286.         // compare the newly calculated line start with the old value
  287.         // if the new line start comes before the old line start, insert a new element
  288.         oldLineInfo = *pLine;
  289.         if ((lineIndex > pWE->nLines) || (lineInfo.lineStart < oldLineInfo.lineStart)) 
  290.         {
  291.             if ((err = _WEInsertLine(lineIndex, &lineInfo, pWE)) != noErr)
  292.                 goto cleanup;
  293.         }
  294.         else
  295.         {
  296.             // overwrite the old element
  297.             pLine->lineStart = lineInfo.lineStart;
  298.             pLine->lineOrigin = lineInfo.lineOrigin;
  299.  
  300.             // remove all further elements which have a lineStart field
  301.             // less than or equal to the current one
  302.             while((lineIndex < pWE->nLines) && (lineInfo.lineStart >= (pLine + 1)->lineStart))
  303.                 _WERemoveLine(lineIndex + 1, pWE);
  304.  
  305.             // if the new line start is the same as the old one...
  306.             if (lineInfo.lineStart == oldLineInfo.lineStart) 
  307.             {
  308.                 // ...and recalThreshold has been reached, we can stop recalculating line breaks
  309.                 if (lineInfo.lineStart >= recalThreshold) 
  310.                 {
  311.                     // although line breaks need not be changed from lineIndex on,
  312.                     // the lineOrigin fields may need to be changed
  313.                     if (lineInfo.lineOrigin != oldLineInfo.lineOrigin) 
  314.                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  315.                     
  316.                     // exit from the line breaking loop
  317.                     goto cleanup;
  318.                 }
  319.             }
  320.             else
  321.             {
  322.                 // otherwise, the new line start comes after the old line start...
  323.                 // if the current line is the one preceding startLine, warn our caller about this
  324.                 if ((lineIndex > 0) && (lineIndex == *startLine)) 
  325.                     *startLine = lineIndex - 1;
  326.             }
  327.         }
  328.     } while(lineInfo.lineStart < pWE->textLength);
  329.     
  330. cleanup:
  331.     // calculate total text height
  332.     textHeight = WEGetHeight(0, pWE->nLines, hWE);
  333.  
  334.     // quirk: if the last character in the text is a carriage return, the caret appears
  335.     // below the last line, so in this case we need to add the extra height to textHeight
  336.     if (WEGetChar(pWE->textLength - 1, hWE) == kEOL)
  337.         textHeight += WEGetHeight(pWE->nLines - 1, pWE->nLines, hWE);
  338.     
  339.     // if total text height has changed, remember to call the scroll callback later
  340.     if (textHeight != pWE->destRect.bottom - pWE->destRect.top)
  341.         BSET(pWE->flags, weFDestRectChanged);
  342.     
  343.     // set destRect.bottom to destRect.top + total text height
  344.     pWE->destRect.bottom = pWE->destRect.top + textHeight;
  345.  
  346.     // return through endLine the index of the last line affected by recalculation
  347.     *endLine = lineIndex - 1;
  348.  
  349.     // make sure startLine isn't greater than endLine
  350.     if (*startLine > *endLine) 
  351.         *startLine = *endLine;
  352.     
  353.     // unlock the text
  354.     _WESetHandleLock(pWE->hText, saveTextLock);
  355.  
  356.     // restore the QuickDraw environment
  357.     _WERestoreQDEnvironment(&saveEnvironment);
  358.  
  359.     // return result code
  360.     return err;
  361. }
  362.  
  363. static Boolean SLCalcSlop(LinePtr pLine, const WERunAttributes *pAttrs,
  364.         Ptr pSegment, long segmentStart, long segmentLength,
  365.         JustStyleCode styleRunPosition, void *callbackData);
  366.  
  367. static Boolean SLCalcSlop(LinePtr pLine, const WERunAttributes *pAttrs,
  368.         Ptr pSegment, long segmentStart, long segmentLength,
  369.         JustStyleCode styleRunPosition, void *callbackData)
  370. {
  371.     struct SLCalcSlopData *p = (struct SLCalcSlopData *) callbackData;
  372.     short segmentWidth;
  373.     Fixed segmentProportion;
  374.     Boolean isEndOfLine;
  375.  
  376.     // see if this text segment ends with a carriage return, or if we've reached the
  377.     // end of the text (in which case we don't want any justification to take place)
  378.     isEndOfLine = (segmentStart + segmentLength >= p->pWE->textLength) ||
  379.                     ( *((Ptr)pSegment + segmentLength - 1) == kEOL);
  380.  
  381.     // if this is the first segment on the line, reset line totals
  382.     if (IS_FIRST_RUN(styleRunPosition)) 
  383.     {
  384.         p->totalSlop = p->lineWidth;
  385.         p->totalProportion = 0;
  386.     }
  387.  
  388. #if WASTE_OBJECTS
  389.     if (pAttrs->runStyle.tsObject != NULL)
  390.     {
  391.         // EMBEDDED OBJECT
  392.         // segment width is just object width; no extra space can be applied for justification
  393.         segmentWidth = (*pAttrs->runStyle.tsObject)->objectSize.h;
  394.         segmentProportion = 0;
  395.     }
  396.     else
  397. #endif
  398.     {
  399.         // REGULAR TEXT
  400.  
  401.         // if this is the last segment on the line, strip trailing spaces
  402.         if (IS_LAST_RUN(styleRunPosition))
  403.         {
  404.             segmentLength = VisibleLength(pSegment, segmentLength);
  405.         }
  406.         // measure this segment
  407.         segmentWidth = TextWidth(pSegment, 0, segmentLength);
  408.  
  409.         // calculate the proportion of extra space to apply to this text segment
  410.         segmentProportion = PortionLine(pSegment, segmentLength,
  411.                             styleRunPosition, kOneToOneScaling, kOneToOneScaling);
  412.     }
  413.     
  414.     // keep track of line totals
  415.     p->totalSlop -= segmentWidth;
  416.     p->totalProportion += segmentProportion;
  417.  
  418.     // if this is the last segment on the line, save values in the line array
  419.     if (IS_LAST_RUN(styleRunPosition))
  420.     {
  421.         // make sure slop is non-negative
  422.         if (p->totalSlop < 0)
  423.             p->totalSlop = 0;
  424.         pLine->lineSlop = p->totalSlop;
  425.  
  426.         if (isEndOfLine) 
  427.         {
  428.             pLine->lineJustAmount = 0;
  429.         }
  430.         else
  431.         {
  432.             pLine->lineJustAmount = FixDiv(BSL(p->totalSlop, 16), p->totalProportion);
  433.         }
  434.     }
  435.     return false;    // keep looping
  436. }
  437.  
  438. pascal void _WERecalSlops(long firstLine, long lastLine, WEHandle hWE)
  439. {
  440.     // Calculates the lineSlop and lineJustAmount fields
  441.     // of the line array for the specified lines
  442.  
  443.     WEPtr pWE = *hWE;
  444.     struct SLCalcSlopData callbackData;
  445.  
  446.     // we only need to bother if the user isn't using left justification
  447.     if (pWE->alignment == weFlushLeft)
  448.         return;
  449.  
  450.     // calculate slop and normalized slop proportion for all lines
  451.     callbackData.pWE = pWE;
  452.     callbackData.lineWidth = pWE->destRect.right - pWE->destRect.left;
  453.     _WESegmentLoop(firstLine, lastLine, SLCalcSlop, (void *) &callbackData, hWE);
  454. }
  455.  
  456. pascal OSErr WEUseText(Handle text, WEHandle hWE)
  457. {
  458.     WEPtr pWE;
  459.     long textLength;
  460.     Boolean saveWELock;
  461.  
  462.     // lock the WE record
  463.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  464.     pWE = *hWE;
  465.  
  466.     // install the text
  467.     _WEForgetHandle(&pWE->hText);
  468.     pWE->hText = text;
  469.     textLength = GetHandleSize(text);
  470.     pWE->textLength = textLength;
  471.     (*pWE->hRuns)[pWE->nRuns].runStart = textLength + 1;
  472.     (*pWE->hLines)[pWE->nLines].lineStart = textLength;
  473.     
  474.     // unlock the WE record
  475.     _WESetHandleLock((Handle) hWE, saveWELock);
  476.  
  477.     return noErr;
  478. }
  479.  
  480. pascal char WEGetAlignment(WEHandle hWE)
  481. {
  482.     return (*hWE)->alignment;
  483. }
  484.  
  485. pascal void WEGetSelection(long *selStart, long *selEnd, WEHandle hWE)
  486. {
  487.     WEPtr pWE = *hWE;
  488.  
  489.     *selStart = pWE->selStart;
  490.     *selEnd = pWE->selEnd;
  491. }
  492.  
  493. pascal void WESetDestRect(const LongRect *destRect, WEHandle hWE)
  494. {
  495.     (*hWE)->destRect = *destRect;
  496. }
  497.  
  498. pascal void WEGetDestRect(LongRect *destRect, WEHandle hWE)
  499. {
  500.     *destRect = (*hWE)->destRect;
  501. }
  502.  
  503. pascal void WESetViewRect(const LongRect *viewRect, WEHandle hWE)
  504. {
  505.     WEPtr pWE = *hWE;
  506.     Rect r;
  507.     
  508.     pWE->viewRect = *viewRect;
  509.  
  510.     // keep the viewRgn in sync with the view rectangle
  511.     WELongRectToRect(viewRect, &r);
  512.     RectRgn(pWE->viewRgn, &r);
  513. }
  514.  
  515. pascal void WEGetViewRect(LongRect *viewRect, WEHandle hWE)
  516. {
  517.     *viewRect = (*hWE)->viewRect;
  518. }
  519.  
  520. pascal long WEGetTextLength(WEHandle hWE)
  521. {
  522.     return (*hWE)->textLength;
  523. }
  524.  
  525. pascal long WECountLines(WEHandle hWE)
  526. {
  527.     return (*hWE)->nLines;
  528. }
  529.  
  530. pascal long WEGetHeight(long startLine, long endLine, WEHandle hWE)
  531. {
  532.     WEPtr pWE = *hWE;
  533.     LineArrayPtr pLines = *pWE->hLines;
  534.     long nLines = pWE->nLines;
  535.  
  536.     startLine = _WEPinInRange(startLine, 0, nLines);
  537.     endLine = _WEPinInRange(endLine, 0, nLines);
  538.     _WEReorder(&startLine, &endLine);
  539.     return pLines[endLine].lineOrigin - pLines[startLine].lineOrigin;
  540. }
  541.  
  542. pascal Handle WEGetText(WEHandle hWE)
  543. {
  544.     return (*hWE)->hText;
  545. }
  546.  
  547. pascal short WEGetChar(long offset, WEHandle hWE)
  548. {
  549.     WEPtr pWE = *hWE;
  550.  
  551.     // sanity check: make sure offset is withing allowed bounds
  552.     if ((offset < 0) || (offset >= pWE->textLength))
  553.     {
  554.         return 0;
  555.     }
  556.  
  557.     // get the specified character (actually, byte)
  558.     return (*pWE->hText)[offset];
  559. }